﻿using System;
using System.Collections.Generic;
using LaoLuo.Time;

namespace LaoLuo
{
    /// <summary>
    /// provides static methods for <see cref="DateTime"/>
    /// </summary>
    public static class DateTimeHelper
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="dateTime1"></param>
        /// <param name="dateTime2"></param>
        /// <returns></returns>
        public static string GetDiffDescription(this DateTime dateTime1, DateTime dateTime2)
        {
            if (dateTime1 > dateTime2)
            {
                var temp = dateTime1;
                dateTime1 = dateTime2;
                dateTime2 = temp;
            }

            var yDiff = dateTime2.Year - dateTime1.Year;
            var mDiff = dateTime2.Month - dateTime1.Month;

            if (mDiff < 0)
            {
                --yDiff;
                mDiff += 12;
            }

            return yDiff > 0 ? string.Format("{0}年{1}个月", yDiff, mDiff) : (mDiff == 0 ? "一段时间" : string.Format("{0}个月", mDiff));
        }

        public static string ToString(this DateTime? value, string format = "yyyy-MM-dd HH:mm", string defaultString = "")
        {
            return DateTimeFormatter.GetDateTimeFormatter(format, defaultString).Format(value);
        }

        public static Interval<DateTime> GetMonthInterval(this DateTime date)
        {
            return Interval<DateTime>.NewClosedInterval(date.ChangeDate(1),
                                                        date.ChangeDate(
                                                            DateTime.DaysInMonth(date.Year,
                                                                                 date.Month)));
        }

        /// <summary>
        /// Gets a interval of the <see cref="date"/>'s calendar
        /// </summary>
        /// <param name="date"></param>
        /// <param name="firstDayOfWeek">which one in <see cref="DayOfWeek"/> is the first day of week</param>
        /// <param name="requiredDayCount">number of days that is required</param>
        /// <returns>a closed interval that has the begin and end date of the <see cref="date"/>'s calendar</returns>
        public static Interval<DateTime> GetCalendarBeginEndDay(this DateTime date, DayOfWeek firstDayOfWeek = DayOfWeek.Sunday, int requiredDayCount = 42)
        {
            if (date.Year < 2)
            {
                throw new ArgumentOutOfRangeException("date", "date's year must be larger than 1");
            }

            var day1 = date.ChangeDate(1);
            var displayFirstDay = day1;
            if (day1.DayOfWeek != firstDayOfWeek)
            {
                displayFirstDay = GetLatestDate(firstDayOfWeek, day1);
            }

            return Interval<DateTime>.NewClosedInterval(displayFirstDay.ChangeTime(0), (displayFirstDay + TimeSpan.FromDays(requiredDayCount)).ChangeTime(0));
        }

        /// <summary>
        /// 获取时间的文字描述,例如"5分钟前"
        /// </summary>
        /// <param name="time">时间</param>
        /// <returns>文字描述</returns>
        public static string GetDescription(this DateTime time)
        {
            var ts = DateTime.Now - time;

            if (ts.TotalMinutes < 1)
            {
                return TimeDescriptionLang.JustNow;
            }

            if (ts.TotalMinutes < 60)
            {
                return (int)ts.TotalMinutes + TimeDescriptionLang.Minutes + TimeDescriptionLang.Ago;
            }

            if (ts.TotalHours < 24)
            {
                return (int)ts.TotalHours + TimeDescriptionLang.Hours + TimeDescriptionLang.Ago;
            }

            return time.ToString("yyyy-MM-dd");
        }

        /// <summary>
        /// 获取日期是否是周六或周日
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool IsWeekend(this DateTime value)
        {
            return value.DayOfWeek == DayOfWeek.Saturday || value.DayOfWeek == DayOfWeek.Sunday;
        }

        /// <summary>
        /// 将DayOfWeek的值转换为中文
        /// </summary>
        /// <param name="value"></param>
        /// <param name="dayOfWeekPrefix"></param>
        /// <returns></returns>
        public static string ToChinese(this DayOfWeek value, string dayOfWeekPrefix = "周")
        {
            var intValue = (int)value;

            if (intValue > 7 || intValue < 0)
            {
                throw new ArgumentOutOfRangeException("value", "value超出范围");
            }

            if (intValue == 7)
            {
                intValue = 0;
            }

            var ret = System.Globalization.DateTimeFormatInfo.CurrentInfo.DayNames[intValue];

            if (dayOfWeekPrefix != "星期")
            {
                ret = ret.Replace("星期", dayOfWeekPrefix);
            }

            return ret;
        }

        /// <summary>
        /// Gets a new <see cref="DateTime"/> with this <see cref="DateTime"/>'s time, and specified date
        /// </summary>
        /// <param name="value"><see cref="DateTime"/> to change date</param>
        /// <param name="other">this <see cref="DateTime"/>'s date part will be used to get the new <see cref="DateTime"/></param>
        /// <returns>a new <see cref="DateTime"/></returns>
        public static DateTime ChangeDate(this DateTime value, DateTime other)
        {
            return new DateTime(other.Year, other.Month, other.Day, value.Hour, value.Minute, value.Second);
        }

        /// <summary>
        /// Gets a new <see cref="DateTime"/> with this <see cref="DateTime"/>'s time, and specified date
        /// </summary>
        /// <param name="value"><see cref="DateTime"/> to change date</param>
        /// <param name="day">day of the new <see cref="DateTime"/></param>
        /// <param name="month">month of the new <see cref="DateTime"/></param>
        /// <param name="year">year of the new <see cref="DateTime"/></param>
        /// <returns>a new <see cref="DateTime"/></returns>
        public static DateTime ChangeDate(this DateTime value, int? day, int? month = null, int? year = null)
        {
            return new DateTime(year ?? value.Year, month ?? value.Month, day ?? value.Day, value.Hour, value.Minute, value.Second);
        }

        /// <summary>
        /// 获取一个新的DateTime,年月为参数指定,其他为本身的值
        /// </summary>
        /// <param name="value"></param>
        /// <param name="other"></param>
        /// <returns></returns>
        public static DateTime ChangeYearAndMonth(this DateTime value, DateTime other)
        {
            return value.ChangeDate(null, other.Month, other.Year);
        }

        /// <summary>
        /// 获取一个新的DateTime,时间为指定的时间
        /// </summary>
        /// <param name="value">日期</param>
        /// <param name="time">时间</param>
        /// <returns>新的DateTime</returns>
        public static DateTime ChangeTime(this DateTime value, string time)
        {
            ThrowHelper.ThrowIfNullOrWhiteSpace(time);

            DateTime ret;

            if (!DateTime.TryParse(value.ToShortDateString() + " " + time.Trim(), out ret))
            {
                throw new ArgumentException();
            }

            return ret;
        }

        /// <summary>
        /// 获取一个新的DateTime,时间为指定的时间
        /// </summary>
        /// <param name="value"></param>
        /// <param name="hour"></param>
        /// <param name="minute"></param>
        /// <param name="second"></param>
        /// <returns></returns>
        public static DateTime ChangeTime(this DateTime value, int hour, int minute = 0, int second = 0)
        {
            return new DateTime(value.Year, value.Month, value.Day, hour, minute, second);
        }

        /// <summary>
        /// 获取一个新的DateTime,时间为指定的DateTime的时间
        /// </summary>
        /// <param name="value"></param>
        /// <param name="other"></param>
        /// <returns></returns>
        public static DateTime ChangeTime(this DateTime value, DateTime other)
        {
            return ChangeTime(value, other.Hour, other.Minute, other.Second);
        }

        /// <summary>
        /// 获取指定时间与当前时间是否为同一天
        /// </summary>
        /// <param name="value">时间</param>
        /// <param name="other">时间</param>
        /// <returns>是否为同一天</returns>
        public static bool IsSameDateWith(this DateTime value, DateTime other)
        {
            return value.Year == other.Year && value.Month == other.Month && value.Day == other.Day;
        }

        /// <summary>
        /// 获取最近的(小于等于指定时间的日期,如果不指定时间则为当前时间),DayOfWeek值为指定值的日期
        /// </summary>
        /// <param name="value"></param>
        /// <param name="basePoint"></param>
        /// <returns></returns>
        public static DateTime GetLatestDate(DayOfWeek value, DateTime? basePoint = null)
        {
            if (!basePoint.HasValue)
            {
                basePoint = DateTime.Now;
            }

            for (int i = 0; i < 7; i++)
            {
                var ret = basePoint.Value.AddDays(-i);

                if (ret.DayOfWeek == value)
                {
                    return ret;
                }
            }

            return default(DateTime);
        }

        /// <summary>
        /// 获取最近的时间为指定时间的DateTime
        /// </summary>
        /// <param name="timeString"></param>
        /// <returns></returns>
        public static DateTime GetLatestTime(string timeString)
        {
            var now = DateTime.Now;

            var ret = now.ChangeTime(timeString);

            if (ret <= now)
            {
                return ret;
            }

            return now.AddDays(-1).ChangeTime(timeString);
        }
    }
}
